package jvn.coord;

import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.LinkedList;

import jvn.client.JvnException;
import jvn.client.interfaces.JvnObjectI;
import jvn.client.interfaces.JvnRemoteServerI;

import org.apache.log4j.Logger;

public class DistributedObject {
	private JvnObjectI object;
	private String name;
	private LinkedList<JvnRemoteServerI> lockReadClients;
	private JvnRemoteServerI lockWrite;

	final static private Logger logger = Logger.getLogger(DistributedObject.class);

	public DistributedObject() {
		super();
		this.object = null;
		this.name = null;
		this.lockReadClients = new LinkedList<JvnRemoteServerI>();
		this.lockWrite = null;
	}

	public DistributedObject(JvnObjectI object, String name) {
		super();
		this.object = object;
		this.name = name;
		this.lockReadClients = new LinkedList<JvnRemoteServerI>();
		this.lockWrite = null;
	}

	public void lockRead(JvnRemoteServerI jvnRemoteServerI) {
		if (object == null || name == null) {
			logger.warn("Appel de lockRead invalide.");
		}
		if (this.lockWrite != null) {
			try {
				logger.debug("lockWrtie : demande de relacher à 1 objet");
				this.object
						.jvnSetObjectState(this.lockWrite.jvnInvalidateWriterForReader(this.object.jvnGetObjectId()));
				logger.debug("lockWrite : demande aboutie");

				// par contre, il reste vérouillé en lecture.
				lockReadClients.add(lockWrite);
				// il n'y a plus de verrou en écriture.
				this.lockWrite = null;
			} catch (RemoteException e) {
				logger.warn("Un client a quitté");
				this.lockWrite = null;
			} catch (JvnException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (!lockReadClients.contains(jvnRemoteServerI)) {
			this.lockReadClients.add(jvnRemoteServerI);
		} else {
			logger.warn("Demande de lockRead alors qu'il est déja prit");
		}
	}

	public void lockWrite(JvnRemoteServerI jvnRemoteServerI) {
		if (object == null || name == null) {
			logger.warn("Appel de lockWrite invalide.");
		}

		Iterator<JvnRemoteServerI> itReadClient = this.lockReadClients.iterator();
		JvnRemoteServerI lockReadClient;

		/**
		 * On invalide les lecteurs
		 */
		while (itReadClient.hasNext()) {
			// on ne rapelle pas le meme client pour invalider son écriture.

			lockReadClient = itReadClient.next();
			if (!lockReadClient.equals(jvnRemoteServerI)) {

				try {
					logger.debug("lockRead : demande de relacher à 1 objet");
					lockReadClient.jvnInvalidateReader(this.object.jvnGetObjectId());
					logger.debug("lockRead : demande aboutie");

				} catch (RemoteException e) {
					try {
						logger.warn("Un client a quitté");
						itReadClient.remove(); // il n'est plus lecteur
						continue;
					} catch (Exception e1) {
						// déja supprimé, ce n'est pas important
					}
				} catch (JvnException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			try {
				itReadClient.remove(); // il n'est plus lecteur
			} catch (Exception e) {
				// déja supprimé, ce n'est pas important
			}
		}

		/**
		 * On invalide l'écrivains éventuel.
		 */
		if (this.lockWrite != null) {
			try {
				this.lockWrite.jvnInvalidateWriter(this.object.jvnGetObjectId());
			} catch (RemoteException e) {
				//e.printStackTrace();
				logger.warn("Un client a quitté");
				lockWrite = null;
			} catch (JvnException e) {
				//e.printStackTrace();
			}
		}
		this.lockWrite = jvnRemoteServerI;
	}

	public JvnObjectI getObject() {
		return object;
	}

	public void setObject(JvnObjectI object) {
		this.object = object;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void unregister(JvnRemoteServerI js) {
		if (lockWrite.equals(js)) {
			lockWrite = null;
		}

		Iterator<JvnRemoteServerI> it = this.lockReadClients.iterator();
		while (it.hasNext()) {
			if (js.equals(it.next())) {
				it.remove();
			}
		}
	}

}
